
<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <title>7.2.&nbsp;个案研究：街道地址</title>
      <link rel="stylesheet" href="../diveintopython.css" type="text/css">
      <link rev="made" href="mailto:f8dy@diveintopython.org">
      <meta name="generator" content="DocBook XSL Stylesheets V1.52.2">
      <meta name="keywords" content="Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free">
      <meta name="description" content="Python from novice to pro">
      <link rel="home" href="../toc/index.html" title="Dive Into Python">
      <link rel="up" href="index.html" title="第&nbsp;7&nbsp;章&nbsp;正则表达式">
      <link rel="previous" href="index.html" title="第&nbsp;7&nbsp;章&nbsp;正则表达式">
      <link rel="next" href="roman_numerals.html" title="7.3.&nbsp;个案研究：罗马字母">
   </head>
   <body>
      <table id="Header" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td id="breadcrumb" colspan="5" align="left" valign="top">导航：<a href="../index.html">起始页</a>&nbsp;&gt;&nbsp;<a href="../toc/index.html">Dive Into Python</a>&nbsp;&gt;&nbsp;<a href="index.html">正则表达式</a>&nbsp;&gt;&nbsp;<span class="thispage">个案研究：街道地址</span></td>
            <td id="navigation" align="right" valign="top">&nbsp;&nbsp;&nbsp;<a href="index.html" title="上一页: “正则表达式”">&lt;&lt;</a>&nbsp;&nbsp;&nbsp;<a href="roman_numerals.html" title="下一页: “个案研究：罗马字母”">&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3" id="logocontainer">
               <h1 id="logo"><a href="../index.html" accesskey="1">深入 Python :Dive Into Python 中文版</a></h1>
               <p id="tagline">Python 从新手到专家 [Dip_5.4b_CPyUG_Release]</p>
            </td>
            <td colspan="3" align="right">
               <form id="search" method="GET" action="http://www.google.com/custom">
                  <p><label for="q" accesskey="4">Find:&nbsp;</label><input type="text" id="q" name="q" size="20" maxlength="255" value=""> <input type="submit" value="搜索"><input type="hidden" name="domains" value="woodpecker.org.cn/diveintopython"><input type="hidden" name="sitesearch" value="www.woodpecker.org.cn/diveintopython"></p>
               </form>
            </td>
         </tr>
      </table>
      <!--#include virtual="/inc/ads" -->
      <div class="section" lang="zh_cn">
         <div class="titlepage">
            <div>
               <div>
                  <h2 class="title"><a name="re.matching"></a>7.2.&nbsp;个案研究：街道地址
                  </h2>
               </div>
            </div>
            <div></div>
         </div>
         <div class="abstract">
            <p>这一系列的例子是由我几年前日常工作中的现实问题启发而来的，当时我需要从一个老化系统中导出街道地址，在将它们导入新的系统之前，进行清理和标准化。(看，我不是只将这些东西堆到一起，它有实际的用处。)这个例子展示我如何处理这个问题。</p>
         </div>
         <div class="example"><a name="d0e17108"></a><h3 class="title">例&nbsp;7.1.&nbsp;在字符串的结尾匹配</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s = <span class='pystring'>'100 NORTH MAIN ROAD'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s.replace(<span class='pystring'>'ROAD'</span>, <span class='pystring'>'RD.'</span>)</span>               <a name="re.matching.1.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<span class="computeroutput">'100 NORTH MAIN RD.'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s = <span class='pystring'>'100 NORTH BROAD ROAD'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s.replace(<span class='pystring'>'ROAD'</span>, <span class='pystring'>'RD.'</span>)</span>               <a name="re.matching.1.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
<span class="computeroutput">'100 NORTH BRD. RD.'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s[:-4] + s[-4:].replace(<span class='pystring'>'ROAD'</span>, <span class='pystring'>'RD.'</span>)</span> <a name="re.matching.1.3"></a><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12">
<span class="computeroutput">'100 NORTH BROAD RD.'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput"><span class='pykeyword'>import</span> re</span>                              <a name="re.matching.1.4"></a><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(<span class='pystring'>'ROAD$'</span>, <span class='pystring'>'RD.'</span>, s)</span>              <a name="re.matching.1.5"></a><img src="../images/callouts/5.png" alt="5" border="0" width="12" height="12"> <a name="re.matching.1.6"></a><img src="../images/callouts/6.png" alt="6" border="0" width="12" height="12">
<span class="computeroutput">'100 NORTH BROAD RD.'</span></pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">我的目标是将街道地址标准化，<tt class="literal">'ROAD'</tt> 通常被略写为 <tt class="literal">'RD.'</tt>。乍看起来，我以为这个太简单了，只用字符串的方法 <tt class="function">replace</tt> 就可以了。毕竟，所有的数据都已经是大写的了，因此大小写不匹配将不是问题。并且，要搜索的串<tt class="literal">'ROAD'</tt>是一个常量，在这个迷惑的简单例子中，<tt class="function">s.replace</tt> 的确能够胜任。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">不幸的是，生活充满了特例，并且我很快就意识到这个问题。比如：<tt class="literal">'ROAD'</tt> 在地址中出现两次，一次是作为街道名称 <tt class="literal">'BROAD'</tt> 的一部分，一次是作为 <tt class="literal">'ROAD'</tt> 本身。<tt class="function">replace</tt> 方法遇到这两处的<tt class="literal">'ROAD'</tt>并没有区别，因此都进行了替换，而我发现地址被破坏掉了。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.3"><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">为了解决在地址中出现多次<tt class="literal">'ROAD'</tt>子串的问题，有可能采用类似这样的方法：只在地址的最后四个字符中搜索替换 <tt class="literal">'ROAD'</tt> (s[-4:])，忽略字符串的其他部分 (s[:-4])。但是，你可能发现这已经变得不方便了。例如，该模式依赖于你要替换的字符串的长度了 (如果你要把 <tt class="literal">'STREET'</tt> 替换为 <tt class="literal">'ST.'</tt>，你需要利用 <tt class="literal">s[:-6]</tt> 和 <tt class="literal">s[-6:].replace(...)</tt>)。你愿意在六月个期间回来调试它们么？我本人是不愿意的。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.4"><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">是时候转到正则表达式了。在 <span class="application">Python</span> 中，所有和正则表达式相关的功能都包含在 <tt class="filename">re</tt> 模块中。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.5"><img src="../images/callouts/5.png" alt="5" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">来看第一个参数：<tt class="literal">'ROAD$'</tt>。这个正则表达式非常简单，只有当 <tt class="literal">'ROAD'</tt> 出现在一个字符串的尾部时才会匹配。字符<tt class="literal">$</tt>表示“字符串的末尾”(还有一个对应的字符，尖号<tt class="literal">^</tt>，表示“字符串的开始”)。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.1.6"><img src="../images/callouts/6.png" alt="6" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">利用 <tt class="function">re.sub</tt> 函数，对字符串 <tt class="varname">s</tt> 进行搜索，满足正则表达式 <tt class="literal">'ROAD$'</tt> 的用 <tt class="literal">'RD.'</tt> 替换。这样将匹配字符串 <tt class="varname">s</tt> 末尾的 <tt class="literal">'ROAD'</tt>，而不会匹配属于单词 <tt class="literal">'ROAD'</tt> 一部分的 <tt class="literal">'ROAD'</tt>，这是因为它是出现在 <tt class="varname">s</tt> 的中间。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <p>继续我的清理地址的故事。很快我发现，在上面的例子中，仅仅匹配地址末尾的 <tt class="literal">'ROAD'</tt> 不是很好，因为不是所有的地址都包括表示街道的单词 (<tt class="literal">'ROAD'</tt>)；有一些直接以街道名结尾。大部分情况下，不会遇到这种情况，但是，如果街道名称为 <tt class="literal">'BROAD'</tt>，那么正则表达式将会匹配 <tt class="literal">'BROAD'</tt> 的一部分为 <tt class="literal">'ROAD'</tt>，而这并不是我想要的。
         </p>
         <div class="example"><a name="d0e17300"></a><h3 class="title">例&nbsp;7.2.&nbsp;匹配整个单词</h3><pre class="screen">
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s = <span class='pystring'>'100 BROAD'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(<span class='pystring'>'ROAD$'</span>, <span class='pystring'>'RD.'</span>, s)</span>
<span class="computeroutput">'100 BRD.'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(<span class='pystring'>'\\bROAD$'</span>, <span class='pystring'>'RD.'</span>, s)</span>  <a name="re.matching.2.2"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
<span class="computeroutput">'100 BROAD'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(r<span class='pystring'>'\bROAD$'</span>, <span class='pystring'>'RD.'</span>, s)</span>  <a name="re.matching.2.3"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
<span class="computeroutput">'100 BROAD'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">s = <span class='pystring'>'100 BROAD ROAD APT. 3'</span></span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(r<span class='pystring'>'\bROAD$'</span>, <span class='pystring'>'RD.'</span>, s)</span>  <a name="re.matching.2.4"></a><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12">
<span class="computeroutput">'100 BROAD ROAD APT. 3'</span>
<tt class="prompt">&gt;&gt;&gt; </tt><span class="userinput">re.sub(r<span class='pystring'>'\bROAD\b'</span>, <span class='pystring'>'RD.'</span>, s)</span> <a name="re.matching.2.5"></a><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12">
<span class="computeroutput">'100 BROAD RD. APT 3'</span></pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.2.2"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">我真正想要做的是，当 <tt class="literal">'ROAD'</tt> 出现在字符串的末尾，并且是作为一个独立的单词时，而不是一些长单词的一部分，才对他进行匹配。为了在正则表达式中表达这个意思，你利用 <tt class="literal">\b</tt>，它的含义是“单词的边界必须在这里”。在 <span class="application">Python</span> 中，由于字符 <tt class="literal">'\'</tt> 在一个字符串中必须转义，这会变得非常麻烦。有时候，这类问题被称为“反斜线灾难”，这也是 <span class="application">Perl</span> 中正则表达式比 <span class="application">Python</span> 的正则表达式要相对容易的原因之一。另一方面，<span class="application">Perl</span> 也混淆了正则表达式和其他语法，因此，如果你发现一个 bug，很难弄清楚究竟是一个语法错误，还是一个正则表达式错误。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.2.3"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">为了避免反斜线灾难，你可以利用所谓的“原始字符串”，只要为字符串添加一个前缀 <tt class="literal">r</tt> 就可以了。这将告诉 <span class="application">Python</span>，字符串中的所有字符都不转义；<tt class="literal">'\t'</tt> 是一个制表符，而 <tt class="literal">r'\t'</tt> 是一个真正的反斜线字符 <tt class="literal">'\'</tt>，紧跟着一个字母 <tt class="literal">'t'</tt>。我推荐只要处理正则表达式，就使用原始字符串；否则，事情会很快变得混乱 (并且正则表达式自己也会很快被自己搞乱了)。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.2.4"><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">(一声叹息) 很不幸，我很快发现更多的与我的逻辑相矛盾的例子。在这个例子中，街道地址包含有作为整个单词的<tt class="literal">'ROAD'</tt>，但是它不是在末尾，因为地址在街道命名后会有一个房间号。由于 <tt class="literal">'ROAD'</tt> 不是在每一个字符串的末尾，没有匹配上，因此调用 <tt class="function">re.sub</tt> 没有替换任何东西，你获得的只是初始字符串，这也不是我们想要的。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#re.matching.2.5"><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">为了解决这个问题，我去掉了 <tt class="literal">$</tt> 字符，加上另一个 <tt class="literal">\b</tt>。现在，正则表达式“<span class="quote">匹配字符串中作为整个单词出现的<tt class="literal">'ROAD'</tt></span>”了，不论是在末尾、开始还是中间。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
      </div>
      <table class="Footer" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td width="35%" align="left"><br><a class="NavigationArrow" href="index.html">&lt;&lt;&nbsp;正则表达式</a></td>
            <td width="30%" align="center"><br>&nbsp;<span class="divider">|</span>&nbsp;<a href="index.html#re.intro" title="7.1.&nbsp;概览">1</a> <span class="divider">|</span> <span class="thispage">2</span> <span class="divider">|</span> <a href="roman_numerals.html" title="7.3.&nbsp;个案研究：罗马字母">3</a> <span class="divider">|</span> <a href="n_m_syntax.html" title="7.4.&nbsp;使用 {n,m} 语法">4</a> <span class="divider">|</span> <a href="verbose.html" title="7.5.&nbsp;松散正则表达式">5</a> <span class="divider">|</span> <a href="phone_numbers.html" title="7.6.&nbsp;个案研究：解析电话号码">6</a> <span class="divider">|</span> <a href="summary.html" title="7.7.&nbsp;小结">7</a>&nbsp;<span class="divider">|</span>&nbsp;
            </td>
            <td width="35%" align="right"><br><a class="NavigationArrow" href="roman_numerals.html">个案研究：罗马字母&nbsp;&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3"><br></td>
         </tr>
      </table>
      <div class="Footer">
         <p class="copyright">Copyright © 2000, 2001, 2002, 2003, 2004 <a href="mailto:mark@diveintopython.org">Mark Pilgrim</a></p>
         <p class="copyright">Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007 <a href="mailto:python-cn@googlegroups.com">CPyUG (邮件列表)</a></p>
      </div>
   </body>
</html>